﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Diagnostics;
using System.IO;
using System.Drawing.Drawing2D;
using System.Text;
using System.Windows.Forms;
using AnguaRPGEngine.Library;
using AnguaRPGEngine.Library.Forms;
using AnguaRPGEngine.Library.Controls;
using AnguaRPGEngine.Library.Dice;
using AnguaRPGEngine.Library.Systems;
using AnguaRPGEngine.Library.Configuration;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using AnguaRPGEngine.Library.Interfaces;
using AnguaRPGEngine.Library.Sockets;
using AnguaRPGEngine.Library.Packages;
using AnguaRPGEngine.Library.Events;
using AnguaRPGEngine.Library.Events.Messages;

namespace AnguaRPGEngine.GMClient
{
    public partial class GMClientMainForm : Form
    {
        #region Private Properties / Members
        private IARPGEModule _module; // the currently active module
        private PackageBuffer _Buffer; // the buffer containing the message packets
        private CompositionContainer _Container; // the composition container for MEF
        private DiceTower _diceTower; // this is the dice tower form
        private Form _PlayersForGM; // this is the Players for Game Master form
        private HostSocket _Host; // this is the host socket
        private List<PCClientSocket> _Clients; // this is the list of connected clients
        private System.Timers.Timer _BufferTimer; // this is the buffer timer
        private Process _MapProcess; // this is the map app process
        private ClientSocket _MapSocket; // this is the socket that talks to the map app
        #endregion
        #region Public Properties / Members
        [ImportMany]
        public Lazy<IARPGEModule, IDictionary<string, object>>[] _modules { get; set; } // MEF
        #endregion
        #region ctor (Constructor)
        public GMClientMainForm()
        {
            InitializeComponent();
            GameSystem.Instance.Initialize(true);
            ImageLibrary.Instance.LoadImageLibrary(GameSystem.Instance.PathToData);
            DieRoller.Instance.LoadDiceCollection();
            _Clients = new List<PCClientSocket>();
            _Buffer = new PackageBuffer();
            _BufferTimer = new System.Timers.Timer(1000);
            _BufferTimer.AutoReset = false;
            _BufferTimer.Elapsed += new System.Timers.ElapsedEventHandler(_BufferTimer_Elapsed);
            _BufferTimer.Start();
        }
        #endregion
        #region Private Methods
        #region ProcessBuffer
        private bool ProcessBuffer()
        {
            BufferPackage package = _Buffer.GetMessage();
            if (package != null)
            {


                int cmd = int.Parse(package.Package.Substring(0, package.Package.IndexOf("=")));
                string pkg = package.Package.Remove(0, package.Package.IndexOf("=") + 1);
                switch (cmd)
                {
                    case Constants.WINDOW_LOCATION:
                        WindowPosition wp = Serializer<WindowPosition>.DeserializeFromString(pkg);
                        FormPosition fp = null;
                        try
                        {

                            fp = GameSystem.Instance.ClientConfig.FormPositions.Find(p => p.FormName == "Map");
                        }
                        catch
                        {
                            fp = null;
                        }
                        if (fp != null)
                        {
                            fp.Location = wp.Location;
                            fp.Size = wp.Size;
                            fp.Visible = wp.Visible;
                        }
                        GameSystem.Instance.SaveConfiguration();
                        break;
                    case Constants.ANNOUNCE_PC:
                        string acPkg = _module.AddCharacter(pkg);
                        _MapSocket.Send(Tools.CreatePackage(Constants.PC_UPDATE, acPkg));
                        foreach (PCClientSocket pcClient in _Clients.FindAll(p => p.SocketID != package.ClientSocketGuid))
                        {
                            pcClient.SendMessage(Tools.CreatePackage(Constants.BROADCAST_PC, pkg));
                        }
                        break;
                    case Constants.LOGON_ATTEMPT:
                        LogonAttempt logonAttempt = Serializer<LogonAttempt>.DeserializeFromString(pkg);
                        if (logonAttempt != null)
                        {
                            string strLogonResults = "";
                            LogonResults logonResults = new LogonResults(true, "Welcome");
                            if (!_module.PlayerAllowed(logonAttempt.PlayerGuid))
                            {
                                logonResults = new LogonResults(false, "Player not allowed.");
                            }
                            if (!_module.GameAllowed(logonAttempt.GameGuid))
                            {
                                logonResults = new LogonResults(false, "Wrong game.");
                            }
                            // check client version
                            if (!ClientVersionOk(logonAttempt.ClientVersion))
                            {
                                logonResults = new LogonResults(false, "Wrong version.");
                            }
                            if (!_module.PasswordMatch(logonAttempt.Password))
                            {
                                logonResults = new LogonResults(false, "Wrong password.");
                            }

                            Serializer<LogonResults>.SerializeToString(logonResults, out strLogonResults);
                            _Clients.Find(p => p.SocketID == package.ClientSocketGuid).SendMessage(Tools.CreatePackage(Constants.LOGON_RESULTS, strLogonResults));
                        }
                        break;
                    case Constants.PC_UPDATE:
                        string ucPkg = _module.UpdateCharacter(pkg);
                        _MapSocket.Send(Tools.CreatePackage(Constants.PC_UPDATE, ucPkg));
                        foreach (PCClientSocket pcClient in _Clients.FindAll(p => p.SocketID != package.ClientSocketGuid))
                        {
                            pcClient.SendMessage(Tools.CreatePackage(Constants.BROADCAST_PC, pkg));
                        }
                        break;
                }
                _module.ProcessPackage(package);
                return true;
            }
            return false;
        }
        #endregion
        #region ClientVersionOk
        private bool ClientVersionOk(string clientVersion)
        {
            return true; // not implemented yet - MMalo, need to make an iteration item for this
        }
        #endregion
        #region MEF Stuff
        #region GetContainerFromDirectory
        private CompositionContainer GetContainerFromDirectory()
        {
            var catalog = new AggregateCatalog();
            var thisAssembly =
            new AssemblyCatalog(
            System.Reflection.Assembly.GetExecutingAssembly());

            catalog.Catalogs.Add(thisAssembly);

            catalog.Catalogs.Add(
            new DirectoryCatalog(GameSystem.Instance.PathToDLLs));

            var container = new CompositionContainer(catalog);
            return container;
        }
        #endregion
        #region Compose
        private bool Compose()
        {
            _Container = GetContainerFromDirectory();

            try
            {
                _Container.ComposeParts(this);
            }
            catch
            (CompositionException compException)
            {
                MessageBox.Show(compException.ToString());
                return false;
            }

            return true;
        }
        #endregion
        #endregion
        #region StartGame
        private void StartGame()
        {
            _Host = new HostSocket();
            _Host.OnAccept += new AcceptEventHandler(_Host_OnAccept);
            _Host.StartListening(Properties.Settings.Default.HostPort);
            lblGameStatus.Text = "Running";
            stopToolStripMenuItem.Enabled = true;
            startToolStripMenuItem.Enabled = loadToolStripMenuItem.Enabled = editToolStripMenuItem.Enabled =
            saveAsToolStripMenuItem.Enabled = saveAsToolStripMenuItem.Enabled = false;
        }
        #endregion
        #region StopGame
        private void StopGame()
        {
            foreach (PCClientSocket pCS in _Clients)
            {
                try
                {
                    pCS.Stop();
                    pCS.OnMessageRecieved -= pcs_OnMessageRecieved;
                    pCS.OnPCClientDisconnect -= pcs_OnPCClientDisconnect;
                    pCS.OnPCClientError -= pcs_OnPCClientError;
                }
                catch
                {
                }
            }
            _Clients.Clear();
            try
            {
                _Host.OnAccept -= _Host_OnAccept;
                _Host.Dispose();
            }
            catch
            {
            }
            startToolStripMenuItem.Enabled = loadToolStripMenuItem.Enabled = editToolStripMenuItem.Enabled =
                 saveAsToolStripMenuItem.Enabled = saveAsToolStripMenuItem.Enabled = true;
            stopToolStripMenuItem.Enabled = false;
        }
        #endregion
        #region SetRulesMenu
        private void SetRulesMenu()
        {
            _module.SetRulesMenu(ref rulesToolStripMenuItem);
            ToolStripMenuItem tsmiEditBooks = new ToolStripMenuItem("Edit Books");
            tsmiEditBooks.Click += new EventHandler(tsmiEditBooks_Click);
            rulesToolStripMenuItem.DropDownItems.Add(new ToolStripSeparator());
            rulesToolStripMenuItem.DropDownItems.Add(tsmiEditBooks);
        }
        #endregion
        #endregion
        #region Private Events
        #region _BufferTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        private void _BufferTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            ProcessBuffer();
            _BufferTimer.Start();

        }
        #endregion
        #region Main Form Events
        #region GMClientMainForm_Load(object sender, EventArgs e)
        private void GMClientMainForm_Load(object sender, EventArgs e)
        {
            using (SplashScreen ss = new SplashScreen(string.Format("GM Client v{0}", Application.ProductVersion)))
            {
                ss.ShowDialog();
            }
            #region Form Positioning Application
            FormPosition fp = null;
            try
            {
                fp = GameSystem.Instance.ClientConfig.FormPositions.Find(p => p.FormName == this.Name);
            }
            catch
            {
                fp = null;
            }
            if (fp == null)
            {
                this.Left = 0;
                this.Top = 0;
                this.Width = Screen.PrimaryScreen.Bounds.Width;
                fp = new FormPosition();
                fp.FormName = this.Name;
                fp.Location = this.Location;
                fp.Size = this.Size;
                fp.Visible = this.Visible;
                GameSystem.Instance.ClientConfig.FormPositions.Add(fp);
            }
            else
            {
                this.Location = fp.Location;
                this.Size = fp.Size;
                this.Visible = fp.Visible;
            }
            #endregion
            editToolStripMenuItem.Enabled = false;
            lblGameName.Text = "[No Game Loaded]";
            lblGameStatus.Text = "Idle";
            stopToolStripMenuItem.Enabled = startToolStripMenuItem.Enabled =
            saveAsToolStripMenuItem.Enabled = saveToolStripMenuItem.Enabled = false;
            #region Show The Dice Tower
            _diceTower = new DiceTower(this);
            _diceTower.Show();
            #endregion

            #region MEF Composition
            bool successfulCompose = Compose();
            if (successfulCompose)
            {
                foreach (var export in _modules)
                {
                    var ID = export.Metadata["ID"] as string;
                    IARPGEModule module = export.Value as IARPGEModule;
                    ToolStripMenuItem tsmi = new ToolStripMenuItem(module.Name);
                    tsmi.Click += new EventHandler(tsmi_Click);
                    modulesToolStripMenuItem.DropDownItems.Insert(0, tsmi);
                    if (ID.ToUpper() == Properties.Settings.Default.ModuleID.ToUpper())
                    {
                        _module = module;
                        tsmi.Checked = true;
                    }

                }
            }
            #endregion

            hostPortToolStripMenuItem.Text = string.Format("Host Port [{0}]", Properties.Settings.Default.HostPort);
            lblGameName.Text = _module.GameName.Replace("&", "&&");
            lblModuleName.Text = _module.Name.Replace("&","&&");

            SetRulesMenu();
            #region _PlayersForGM Form
            _PlayersForGM = _module.GetPlayersForGM(this);
            _PlayersForGM.Left = 0;
            _PlayersForGM.Top = 100;
            _PlayersForGM.Show();
            #endregion

            #region Map
            _MapProcess = new Process();
#if DEBUG
            _MapProcess.StartInfo.WorkingDirectory = Directory.GetCurrentDirectory().Replace("GMClient", "Map");
#endif
            _MapProcess.StartInfo.Arguments = string.Format("/M:{0} /P:{1}", Constants.GM_CLIENT, Properties.Settings.Default.MapPort);
            _MapProcess.StartInfo.FileName = "AnguaRPGEngine.Map.exe";
            _MapProcess.Start();
            System.Threading.Thread.Sleep(1000);
            _MapSocket = new ClientSocket();
            _MapSocket.OnReceive += new ReceiveEventHandler(_MapSocket_OnReceive);
            _MapSocket.OnSocketError += new SocketErrorEventHandler(_MapSocket_OnSocketError);
            _MapSocket.OnConnect += new ConnectEventHandler(_MapSocket_OnConnect);
            _MapSocket.StartClient("localhost", Properties.Settings.Default.MapPort);
            _MapSocket.StartConnect();
            fp = null;
            try
            {
                fp = GameSystem.Instance.ClientConfig.FormPositions.Find(p => p.FormName == "Map");
            }
            catch
            {
                fp = null;
            }
            if (fp == null)
            {
                WindowPosition wp = new WindowPosition();
                wp.Location = new Point(this.Left, this.Top + this.Height);
                wp.Size = new Size(300, 300);
                wp.WindowName = "Map";
                wp.Visible = true;
                fp = new FormPosition();
                fp.FormName = "Map";
                fp.Location = wp.Location;
                fp.Size = wp.Size;
                fp.Visible = wp.Visible;
                GameSystem.Instance.ClientConfig.FormPositions.Add(fp);
                string wpSerialized = "";
                Serializer<WindowPosition>.SerializeToString(wp, out wpSerialized);
                _MapSocket.Send(Tools.CreatePackage(Constants.WINDOW_LOCATION, wpSerialized));
            }
            else
            {
                WindowPosition wp = new WindowPosition();
                wp.Location = fp.Location;
                wp.Size = fp.Size;
                wp.WindowName = "Map";
                wp.Visible = true;// fp.Visible;
                string wpSerialized = "";
                Serializer<WindowPosition>.SerializeToString(wp, out wpSerialized);
                _MapSocket.Send(Tools.CreatePackage(Constants.WINDOW_LOCATION, wpSerialized));
            }

            #endregion

        }
        #endregion
        #region GMClientMainForm_LocationChanged(object sender, EventArgs e)
        private void GMClientMainForm_LocationChanged(object sender, EventArgs e)
        {
            ParentFormMoveEvent pfme = new ParentFormMoveEvent(this.Location, this.Size);
            ApplicationController.EventAggregator.SendMessage(pfme);
        }
        #endregion
        #region GMClientMainForm_ResizeEnd(object sender, EventArgs e)
        private void GMClientMainForm_ResizeEnd(object sender, EventArgs e)
        {
            ParentFormMoveEvent pfme = new ParentFormMoveEvent(this.Location, this.Size);
            ApplicationController.EventAggregator.SendMessage(pfme);
        }
        #endregion
        #region GMClientMainForm_FormClosing(object sender, FormClosingEventArgs e)
        private void GMClientMainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            GameSystem.Instance.IsShuttingDown = true;
            _diceTower.Close();
            _PlayersForGM.Close();
            _MapProcess.CloseMainWindow();
            _MapProcess.WaitForExit();
            _BufferTimer.Stop();
            while (ProcessBuffer()) ;
            FormPosition fp = null;
            try
            {
                fp = GameSystem.Instance.ClientConfig.FormPositions.Find(p => p.FormName == this.Name);
            }
            catch
            {
                fp = null;
            }
            if (fp == null)
            {

                fp = new FormPosition();
                fp.FormName = this.Name;
                fp.Location = this.Location;
                fp.Size = this.Size;
                fp.Visible = this.Visible;
                GameSystem.Instance.ClientConfig.FormPositions.Add(fp);
            }
            else
            {
                fp.Location = this.Location;
                fp.Size = this.Size;
                fp.Visible = this.Visible;
            }
            GameSystem.Instance.Shutdown();
        }
        #endregion
        #endregion
        #region Map Socket Events
        #region _MapSocket_OnConnect()
        void _MapSocket_OnConnect()
        {
            _MapSocket.StartReceive();
        }
        #endregion
        #region _MapSocket_OnSocketError(object sender, System.Net.Sockets.SocketException e)
        void _MapSocket_OnSocketError(object sender, System.Net.Sockets.SocketException e)
        {

            MessageBox.Show(e.Message);
        }
        #endregion
        #region _MapSocket_OnReceive(byte[] data, long numBytes)
        void _MapSocket_OnReceive(byte[] data, long numBytes)
        {
            // add to buffer
            _Buffer.AddMessage(new Guid(), Tools.GetStringFromPackage(data, (int)numBytes));

        }
        #endregion
        #endregion
        #region Overrides For Background Painting And Resizing

        //const int WM_NCHITTEST = 0x0084;
        //const int HTCLIENT = 1;
        //const int HTCAPTION = 2;
        //protected override void WndProc(ref Message m)
        //{
        //    base.WndProc(ref m);
        //    switch (m.Msg)
        //    {
        //        case WM_NCHITTEST:
        //            if (m.Result == (IntPtr)HTCLIENT)
        //            {
        //                m.Result = (IntPtr)HTCAPTION;
        //            }
        //            break;
        //    }
        //}
        //protected override void OnPaint(PaintEventArgs e)
        //{
        //    base.OnPaint(e);
        //    e.Graphics.FillRectangle(new LinearGradientBrush(new Point(0, 0), new Point(this.Width, 15), Color.DarkBlue, Color.Blue), new Rectangle(0, 0, this.Width, 15));
        //    e.Graphics.DrawLine(new Pen(new SolidBrush(Color.DarkGray), 1f), new Point(0, 16), new Point(this.Width, 16));
        //    e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(Color.White), new PointF(0, 0));

        //    e.Graphics.DrawString(string.Format("Current Module [{0}] \t Current Game [{1}] \t", _module.Name, _module.GameName), this.Font, new SolidBrush(Color.Yellow), new PointF(0, this.Height - 29));
        //}
        //protected override CreateParams CreateParams
        //{
        //    get
        //    {
        //        CreateParams cp = base.CreateParams;
        //        cp.Style |= 0x40000;

        //        return cp;
        //    }
        //}
        #endregion
        #region Host Socket and Client Socket Events
        #region _Host_OnAccept(ClientSocket cs)
        void _Host_OnAccept(ClientSocket cs)
        {
            cs.StartReceive();
            PCClientSocket pcs = new PCClientSocket(cs);
            pcs.OnMessageRecieved += new PCClientSocket.PCClientMessageReceivedEventHandler(pcs_OnMessageRecieved);
            pcs.OnPCClientDisconnect += new PCClientSocket.PCClientSocketDisconnectEventHandler(pcs_OnPCClientDisconnect);
            pcs.OnPCClientError += new PCClientSocket.PCClientSocketErrorEventHandler(pcs_OnPCClientError);
            pcs.SendMessage(Tools.CreatePackage(Constants.REQUEST_LOGON, ""));
            _Clients.Add(pcs);
        }
        #endregion 
        #region pcs_OnPCClientError(Guid SocketID, string Message)
        void pcs_OnPCClientError(Guid SocketID, string Message)
        {
            throw new NotImplementedException();
        }
        #endregion 
        #region pcs_OnPCClientDisconnect(Guid SocketID)
        void pcs_OnPCClientDisconnect(Guid SocketID)
        {
            throw new NotImplementedException();
        }
        #endregion
        #region pcs_OnMessageRecieved(Guid SocketID, string Message)
        void pcs_OnMessageRecieved(Guid SocketID, string Message)
        {
            _Buffer.AddMessage(SocketID, Message);
        }
        #endregion
        #endregion
        #region Menu Item Click Events
        #region aboutAnguaRPGEngineToolStripMenuItem_Click(object sender, EventArgs e)
        private void aboutAnguaRPGEngineToolStripMenuItem_Click(object sender, EventArgs e)
        {
            using (AboutForm frmAbout = new AboutForm(this.Text))
            {
                frmAbout.ShowDialog();
            }
        }
        #endregion
        #region defaultPositionsAndSizesToolStripMenuItem_Click(object sender, EventArgs e)
        private void defaultPositionsAndSizesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _diceTower.Left = this.Left + this.Width - _diceTower.Width;
            _diceTower.Top = this.Top + this.Height + 10;
        }
        #endregion
        #region  diceTowerToolStripMenuItem_Click(object sender, EventArgs e)
        private void diceTowerToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _diceTower.Visible = true;
            _diceTower.BringToFront();
        }
        #endregion
        #region editToolStripMenuItem_Click(object sender, EventArgs e)
        private void editToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _module.EditGame(this);
            MapFileClass mfc = _module.GetCurrentMap();
            if (mfc == null)
            {
                mfc = new MapFileClass();
                mfc.Initialize();
            }
            string serializedObject = "";
            Serializer<MapFileClass>.SerializeToString(mfc, out serializedObject);
            _MapSocket.Send(Tools.CreatePackage(Constants.FULL_MAP, serializedObject));
            editToolStripMenuItem.Enabled = _module.GameIsLoaded;
            lblGameName.Text = _module.GameName.Replace("&", "&&");
            StopGame();
            this.Refresh();
        }
        #endregion
        #region exitToolStripMenuItem_Click(object sender, EventArgs e)
        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }
        #endregion
        #region hostPortToolStripMenuItem_Click(object sender, EventArgs e)
        private void hostPortToolStripMenuItem_Click(object sender, EventArgs e)
        {
            using (HostSettingsForm frmHostSettings = new HostSettingsForm(Properties.Settings.Default.HostPort))
            {
                if (frmHostSettings.ShowDialog() == DialogResult.OK)
                {
                    Properties.Settings.Default.HostPort = frmHostSettings.HostPort;
                    Properties.Settings.Default.Save();
                }
            }
        }
        #endregion
        #region loadToolStripMenuItem_Click(object sender, EventArgs e)
        private void loadToolStripMenuItem_Click(object sender, EventArgs e)
        {
            StopGame();
            _module.LoadGame();
            MapFileClass mfc = _module.GetCurrentMap();
            if (mfc == null)
            {
                mfc = new MapFileClass();
                mfc.Initialize();
            }
            string serializedObject = "";
            Serializer<MapFileClass>.SerializeToString(mfc, out serializedObject);
            _MapSocket.Send(Tools.CreatePackage(Constants.FULL_MAP, serializedObject));
            editToolStripMenuItem.Enabled = saveToolStripMenuItem.Enabled = saveAsToolStripMenuItem.Enabled = _module.GameIsLoaded;
            lblGameName.Text = _module.GameName.Replace("&", "&&");

            this.Refresh();
        }
        #endregion
        #region newToolStripMenuItem_Click(object sender, EventArgs e)
        private void newToolStripMenuItem_Click(object sender, EventArgs e)
        {
            StopGame();
            _module.NewGame(this);
            MapFileClass mfc = new MapFileClass();
            mfc.Initialize();
            string serializedObject = "";
            Serializer<MapFileClass>.SerializeToString(mfc, out serializedObject);
            _MapSocket.Send(Tools.CreatePackage(Constants.FULL_MAP, serializedObject));
        }
        #endregion  
        #region playersToolStripMenuItem_Click(object sender, EventArgs e)
        private void playersToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _PlayersForGM.Visible = true;
            _PlayersForGM.BringToFront();
        }
        #endregion
        #region saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _module.SaveGame(true);
            lblGameName.Text = _module.GameName.Replace("&", "&&");
        }
        #endregion
        #region saveToolStripMenuItem_Click(object sender, EventArgs e)
        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _module.SaveGame();
        }
        #endregion
        #region startToolStripMenuItem_Click(object sender, EventArgs e)
        private void startToolStripMenuItem_Click(object sender, EventArgs e)
        {
            StartGame();
        }
        #endregion
        #region stopToolStripMenuItem_Click(object sender, EventArgs e)
        private void stopToolStripMenuItem_Click(object sender, EventArgs e)
        {
            StopGame();
        }
        #endregion
        #region tsmi_Click(object sender, EventArgs e)
        private void tsmi_Click(object sender, EventArgs e)
        {
            // load this mod
        }
        #endregion
        #region tsmiEditBooks_Click(object sender, EventArgs e)
        private void tsmiEditBooks_Click(object sender, EventArgs e)
        {
            if (_module.EditBooks() == DialogResult.OK)
            {
                SetRulesMenu();
            }
        }
        #endregion 
        #endregion
        #endregion
    }
}